package com.wlyuan.index.domain.index.factory;

import com.alibaba.fastjson.JSON;
import com.wlyuan.core.domain.valueobj.WaybillId;
import com.wlyuan.index.domain.index.WaybillIndex;
import com.wlyuan.index.domain.index.builder.WaybillIndexBuilder;
import com.wlyuan.index.domain.index.repository.WaybillIndexRepository;
import com.wlyuan.index.domain.source.Source;
import com.wlyuan.index.domain.source.factory.SourceFactory;
import com.wlyuan.index.domain.source.repository.SourceRepository;
import com.wlyuan.index.domain.source.valueobj.SourceId;
import com.wlyuan.lock.annotation.CacheLock;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
@Slf4j
@RequiredArgsConstructor
public class WaybillIndexFactory {
    private final SourceRepository sourceRepository;
    private final WaybillIndexRepository indexRepository;
    private final SourceFactory sourceFactory;
    private static final String LOCK_NAME = "PC::LOCK::INDEX::WAYBILL_NEW";

    private void addIndex(WaybillIndexBuilder builder, WaybillId waybillId) {
        var oldIndex = indexRepository.ofId(waybillId);
        if (null != oldIndex) {
            builder.withIndex(oldIndex);
        }
    }

    private void addBounds(WaybillIndexBuilder builder, Source source) {
        if (CollectionUtils.isEmpty(source.getBoundIds())) {
            return;
        }

        var bounds = sourceRepository.list(source.getBoundIds());
        if (CollectionUtils.isEmpty(bounds)) {
            return;
        }
        bounds.forEach(builder::withBound);
    }

    @CacheLock(cacheName = LOCK_NAME, key = "#waybillId.id", message = "运单索引锁定失败")
    public WaybillIndex create(WaybillId waybillId) {
        var source = sourceRepository.ofId(new SourceId(waybillId));
        if (null == source) {
            source = sourceFactory.create(waybillId);
            logger.info("重建运单索引数据: {}", source);
        }

        if (null == source) {
            logger.error("运单数据不存在: {}", waybillId);
            return null;
        }

        var builder = new WaybillIndexBuilder(source);
        this.addIndex(builder, waybillId);
        this.addBounds(builder, source);
        var index = builder.build();
        indexRepository.save(index);

        if (logger.isDebugEnabled()) {
            logger.debug("运单索引更新: {} {}", waybillId, JSON.toJSONString(index));
        }
        return index;
    }


    private Map<SourceId, Source> mapSources(Collection<WaybillId> waybillIds) {
        var sourceIds = waybillIds.stream()
                .map(SourceId::new)
                .collect(Collectors.toList());
        var sourceMap = sourceRepository.getSourceMap(sourceIds);
        waybillIds.forEach(waybillId -> {
            var sourceId = new SourceId(waybillId);
            if (sourceMap.containsKey(sourceId)) {
                return;
            }
            var source = sourceFactory.create(waybillId);
            logger.warn("重建运单索引数据: {} {}", waybillId, source);
            sourceMap.put(sourceId, sourceFactory.create(waybillId));
        });
        return sourceMap;
    }

    // FIXME: 批量创建索引没有加锁，存在并发冲突问题
    public List<WaybillIndex> create(Collection<WaybillId> waybillIds) {
        if (CollectionUtils.isEmpty(waybillIds)) {
            return Collections.emptyList();
        }

        var sourceMap = this.mapSources(waybillIds);
        if (MapUtils.isEmpty(sourceMap)) {
            return Collections.emptyList();
        }

        var indexMap = indexRepository.indexes(waybillIds);
        var boundsMap = sourceRepository.getBoundsMap(sourceMap.keySet());

        var indexes = new ArrayList<WaybillIndex>();
        sourceMap.forEach((sourceId, source) -> {
            var builder = new WaybillIndexBuilder(source);
            WaybillId waybillId = source.getBusinessId();
            builder.withIndex(indexMap.get(waybillId));
            builder.withBounds(boundsMap.get(sourceId));
            indexes.add(builder.build());
        });

        if (logger.isDebugEnabled()) {
            logger.debug("批量创建运单索引: {} {}", indexes.size(), waybillIds);
        }

        indexRepository.saveAll(indexes);
        return indexes;
    }

    public void remove(WaybillId waybillId) {
        indexRepository.deleteById(waybillId.getId());
    }
}
